/*
 * stnJson.h
 *
 *  Created on: Jul 10, 2013
 *      Author: alexandru
 */

#include <iostream>
#include <stdlib.h>
#include <string>
#include <vector>
#include <sstream>


#ifndef STNJSON_H_
#define STNJSON_H_

using namespace std;


//std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
//		    std::stringstream ss(s);
//		    std::string item;
//		    while (std::getline(ss, item, delim)) {
//		        elems.push_back(item);
//		    }
//		    return elems;
//		}
//
//
//std::vector<std::string> split(const std::string &s, char delim) {
//		std::vector<std::string> elems;
//		split(s, delim, elems);
//		return elems;
//	}


class jsonUtils{
public:
	static std::string dts(double x) {
		    std::stringstream s;
		    s << x;
		    return s.str();
		}
};


//int fast_atoi( const char * str )
//{
//    int val = 0;
//    while( *str ) {
//        val = val*10 + (*str++ - '0');
//    }
//    return val;
//}

//using namespace std;


/*
 * VARIABLE TYPES:
 * String
 * Numbers
 * Booleans
 * Array
 * Objects
 *
 *
 * JSON TYPES:
 * print encode_json({
        number => JSON::Types::number "123",
        string => JSON::Types::string 123,
        bool   => JSON::Types::bool "True value",
    });
 * */










class Object{

private:
	std::vector<Object> objectList;

	const char * propName;

	const char * StringType;
	double NumberType;
	bool BooleanType;

	/*
	 * 1 - String
	 * 2 - Number
	 * 3 - Boolean
	 * 4 - Object
	 * 5 - Array
	 * */

	std::string result;

	int PROP_TYPE;

	int ind;




public:

	bool isRoot;

//        bool isArray;
//        bool isObject
//        bool isArrayElement;



	static int OBJ_MODE;
	static int ARR_MODE;

	void setType(int mode){
		PROP_TYPE=mode;
	}

	int getType(){
		return PROP_TYPE;
	}

	//0 no param
	Object(){

	}



	//1 String
	Object(const char * ppropName, const char * pValue){
		StringType=pValue;
		propName=ppropName;
		NumberType=0;
		BooleanType=false;
		PROP_TYPE=1;
//		isRoot=false;
		};

	//2 Number as double
	Object(const char * ppropName, double pdNumber){
			propName=ppropName;
			NumberType=pdNumber;
			StringType=NULL;
			BooleanType=false;
			PROP_TYPE=2;
//			isRoot=false;
	};

	//2 Number as int
	Object(const char * ppropName, int pdNumber){
			propName=ppropName;
			NumberType=pdNumber;
			StringType=NULL;
			BooleanType=false;
			PROP_TYPE=2;
//			isRoot=false;
	};

	//3 Bolean
		Object(const char * ppropName, bool pbolValue){
					propName=ppropName;
					BooleanType=pbolValue;
					NumberType=0;
					StringType=NULL;
					PROP_TYPE=3;
//					isRoot=false;
			};




		//5 object
		Object(const char * ppropName){
			propName=ppropName;
			NumberType=0;
			StringType=NULL;
			BooleanType=false;
//			isRoot=false;
			PROP_TYPE=4;

		}






	//destructor:
	~Object(){};






	const char * toJson(){

		result.clear();

		switch(PROP_TYPE){
		case 1:

            result+="\"";
			result+=propName;
			result+="\":";

			result+="\"";
			result+=StringType;
			result+="\"";
			break;

		case 2:

				result+="\"";
				result+=propName;
				result+="\":";

//			result+="\"";
			result+=jsonUtils::dts(NumberType);
//			result+="\"";
			break;


		case 3:

			result+="\"";
			result+=propName;
			result+="\":";

//			result+="\"";
			if(BooleanType) result+="true";  else result+="false";
//			result+="\"";
			break;


		case 4:

			if(objectList.size()<=0) {
				return "undefined";
			}



			if(!isRoot){
				result+="\"";
				result+=propName;
				result+="\":";
			}


				result+='{';
				for(int i=0; i<(int)objectList.size()-1; i++){
						objectList.at(i).isRoot=false;
						result+=objectList.at(i).toJson();
						result+=",";
				}
				objectList.at(objectList.size()-1).isRoot=false;
				result+=objectList.at(objectList.size()-1).toJson();
				result+='}';

			break;

			case 5:
					if(objectList.size()<0) {
									return "[]";
								}

					if(!isRoot){
									result+="\"";
									result+=propName;
									result+="\":";
								}


									result+='[';
									for(int i=0; i<(int)objectList.size()-1; i++){
											objectList.at(i).isRoot=true;
											result+=objectList.at(i).getJsonValue();
											result+=",";
									}
									objectList.at(objectList.size()-1).isRoot=true;
									result+=objectList.at(objectList.size()-1).getJsonValue();
									result+=']';

					break;
		}




		return result.c_str();
	}

	void addObject(Object object){
		objectList.push_back(object);
	}

	void push(Object *object){
			objectList.push_back(*object);
		}

	const char * getName(){
		return propName;
	}






	Object getByIndex(string index){
		ind=atoi(index.c_str());
		return objectList.at(ind);//.getByName(restToGet);
	}

//	Object getByName(string input){
//		string str;
//		string restToScan;
//
//			for (unsigned i=0; i<input.length(); ++i)
//			  {
//				char c=input.at(i);
//				std::cout << c << endl;
//				str+=input.at(i);
//
//				if(c='.'){
//					break;
//				}
//
//			  }
//
//
//			cout <<"str = " <<str << endl;
//
//
//				int lim1=str.length()+1;
//				int lim2=input.length();
//				//string::size_type loc = input.find( str, 0 );
//
//				//cout << loc << " "  << lim2 <<  " din "<< input <<  endl;
////				restToScan=input.substr(, lim-(i.length()+1));
//				//cout << "REST TO SCAN: " << restToScan << " length: "<< restToScan.length()<< endl;
//
//
////			 return getByName(str);
//	}



	Object getByName(string objPropName){
		for(int i=0; i<(int)objectList.size(); i++){
								if(objectList.at(i).getName()==objPropName){
									return objectList.at(i);
								}
						};

				return Object("nu exista", "nu exista");
	}









	const char * getJsonValue(){
		result.clear();
            switch(PROP_TYPE){
                case 1:
                	result+="\"";
                	result+=StringType;
                	result+="\"";
                    break;
                case 2:
                	result+=jsonUtils::dts(NumberType).c_str();
                   break;
                case 3:
                   if(BooleanType) result+="true"; else result+="false";
                   break;
                default:
                  return toJson();
            }
		return result.c_str();
	}




	void describe(){}


	inline bool IsWhitespace(char c)
		{
			return (c == '\n' || c == ' ' || c == '\t' || c == '\r' || c == '\f');
		}

	inline bool IsNumber(char c)
	{
		return ((c >= '0' && c <= '9') || c == '.' || c == '-');
	}



	void setName(string str){
		cout << "object named --" << str << "--"<< endl;
		//cout << getJsonValue() << endl;
		propName=str.c_str();
	}


	void setValue(string str){
		cout << "object "<< propName << " set value --| " << str << "--" << endl;

		unsigned b_first = str.find("{");
		unsigned b_last = str.find_last_of("}");

		unsigned a_first = str.find("[");
		unsigned a_last = str.find_last_of("]");

		string res;

		//daca are format {}
			if(b_first==0 && b_last==str.length()-1){
				cout << b_first << " -- " << b_last << " -- " << str.length()-1 << endl;
				res=str.substr(b_first+1, b_last-1);
				cout << "this is object" << endl;
				convertObject(res);
				return;
			}

			//daca are format []
			if(a_first==0 && a_last==str.length()-1){
				cout << a_first << " -- " << a_last << " -- " << str.length()-1 << endl;
				string res=str.substr(a_first+1, a_last-1);
				cout << "this is array" << endl;
				convertArray(res);
				return;
			}


	}


	int nrAcol;
	int nrPaDr;
	bool iPr;
	bool skip;
	string propNamT;
	string propValT;
	char c; //current analized char

	struct tempObject{
		string name;
		string prop;
	};

	vector<tempObject> listaTemp;


	void convertObject(string str){

	nrAcol=0;
	nrPaDr=0;
	iPr=true;
	skip=false;
	propNamT.clear();
	propValT.clear();
	listaTemp.clear();
	PROP_TYPE=4;

		for (unsigned i=0; i<str.length(); ++i){
			if(!IsWhitespace(str.at(i))){
				c=str.at(i);
				//cout << c << endl;
				switch(c){
					case '{':
						nrAcol++; skip=false;
					break;

					case '}':
						nrAcol--; skip=false;
					break;

					case '[':
						nrPaDr++; skip=false;
					break;

					case ']':
						nrPaDr--; skip=false;
					break;


					case ',':
						if(nrAcol==0 && nrPaDr==0){
							iPr=true;
							tempObject tmp;
							tmp.name=propNamT;
							tmp.prop=propValT;
							listaTemp.push_back(tmp);
							propNamT.clear();
							propValT.clear();
							skip=true;

//							cout << "____________________________VIRGUYLA" << endl;

						}
					break;

					case ':':
						iPr=false;
						if(nrAcol==0 && nrPaDr==0){skip=true;}
					break;

					default:
						skip=false;
					break;
				}//exit switch




				if(!skip){
					if(iPr){
						propNamT+=c;
					}else{
						propValT+=c;
					}
				}

				//adding the lastOne:
				if(i==str.length()-1){
					tempObject tmp;
					tmp.name=propNamT;
					tmp.prop=propValT;
					listaTemp.push_back(tmp);
				}


			}//exit if
		}//exit for

		for (int i =0; i<(int)listaTemp.size(); i++){
			Object * obj=new Object();
			obj->setName(listaTemp.at(i).name);
			obj->setValue(listaTemp.at(i).prop);
			push(obj);
		}

	}//exit convert object


	void convertArray(string str){
			cout << "converting Array here" << endl;
			PROP_TYPE=5;
			skip=false;
			propNamT.clear();
			propValT.clear();
			listaTemp.clear();

			for (unsigned i=0; i<str.length(); ++i){
						if(!IsWhitespace(str.at(i))){

							c=str.at(i);

							switch(c){
								case '{':
									nrAcol++; skip=false;
								break;

								case '}':
									nrAcol--; skip=false;
								break;

								case '[':
									nrPaDr++; skip=false;
								break;

								case ']':
									nrPaDr--; skip=false;
								break;


								case ',':
									if(nrAcol==0 && nrPaDr==0){
										skip=true;

										tempObject tmp;
										tmp.name="arrObj";
										tmp.prop=propValT;
										listaTemp.push_back(tmp);
										propValT.clear();
									}
								break;

								default:
									skip=false;
								break;
							}//exit switch
						}//exit if



						if(!skip){
							propValT+=c;
						}

						if(i==str.length()-1){
							tempObject tmp;
							tmp.name="arrObj";
							tmp.prop=propValT;
							listaTemp.push_back(tmp);
						}

			}//exit for

			for (int i =0; i<(int)listaTemp.size(); i++){
						Object * obj=new Object();
						obj->setName(listaTemp.at(i).name);
						obj->setValue(listaTemp.at(i).prop);
						push(obj);
					}

		}//exit convert array





};

















#endif /* STNJSON_H_ */
